#ifdef BUILD_LK
#include <platform/mt_gpio.h> 
#include <platform/mt_pmic.h>
#include <platform/mt_i2c.h>
#include <platform/upmu_common.h>
#include <debug.h>
#else
#include <mt-plat/mt_gpio.h>
#include <mach/gpio_const.h>
#endif
#include "lcm_drv.h"

#ifdef BUILD_LK
#define ICN6211_LOG printf
#define ICN6211_REG_WRITE(add, data) 		icn6211_reg_i2c_write(add, data)
#define ICN6211_REG_READ(add) 				icn6211_reg_i2c_read(add)
#else
#define icn6211_i2c_write(addr, data) 		icn6211_write_bytes(addr, data)
int icn6211_i2c_read(unsigned char addr, u32 *returnData);

#define ICN6211_LOG printk
#define ICN6211_REG_WRITE(add, data) 		icn6211_i2c_write(add, data)
#define ICN6211_REG_READ(add) 				lcm_icn6211_i2c_read(add)
#endif

#ifdef BUILD_LK
#define LCM_LOGI(string, args...)  dprintf(0, "[LK/"LOG_TAG"]"string, ##args)
#define LCM_LOGD(string, args...)  dprintf(1, "[LK/"LOG_TAG"]"string, ##args)
#else
#define LCM_LOGI	printk
#define LCM_LOGD	printk
#endif

#if 0 //ndef BUILD_LK
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/i2c.h>
#include <linux/irq.h>
/* #include <linux/jiffies.h> */
/* #include <linux/delay.h> */
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>

#ifndef CONFIG_FPGA_EARLY_PORTING
#define ICN6211_I2C_BUSNUM  3	/* for I2C channel 2 */电路实际接到3通道
#define I2C_ID_NAME 	"icn6211"
#define ICN6211_ADDR 	0x2c	//(0x58>>1)	//参考右移

#if defined(CONFIG_MTK_LEGACY)
static struct i2c_board_info __initdata icn6211_board_info = { I2C_BOARD_INFO(I2C_ID_NAME, ICN6211_ADDR) };
#endif
#if !defined(CONFIG_MTK_LEGACY)
static const struct of_device_id lcm_of_match[] = {
		{.compatible = "mediatek, I2C_LCD_BIAS"},
		{},
};
#endif

/*static struct i2c_client *tps65132_i2c_client;*/
struct i2c_client *icn6211_i2c_client;

/*****************************************************************************
 * Function Prototype
 *****************************************************************************/
static int icn6211_probe(struct i2c_client *client, const struct i2c_device_id *id);
static int icn6211_remove(struct i2c_client *client);
/*****************************************************************************
 * Data Structure
 *****************************************************************************/
struct icn6211_dev {
	struct i2c_client *client;

};

static const struct i2c_device_id icn6211_id[] = {
	{I2C_ID_NAME, 0},
	{}
};

/* #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) */
/* static struct i2c_client_address_data addr_data = { .forces = forces,}; */
/* #endif */
static struct i2c_driver icn6211_iic_driver = {
	.id_table 	= icn6211_id,
	.probe 		= icn6211_probe,
	.remove 	= icn6211_remove,
	/* .detect	= mt6605_detect, */
	.driver = {
	   .owner 	= THIS_MODULE,
	   .name 	= "icn6211",
#if !defined(CONFIG_MTK_LEGACY)
			.of_match_table = lcm_of_match,
#endif
		   },
};

static int icn6211_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	LCM_LOGI("icn6211_iic_probe\n");
	LCM_LOGI("icn6211: info==>name=%s addr=0x%x\n", client->name, client->addr);
	icn6211_i2c_client = client;
	return 0;
}

static int icn6211_remove(struct i2c_client *client)
{
	LCM_LOGI("icn6211_remove\n");
	icn6211_i2c_client = NULL;
	i2c_unregister_device(client);
	return 0;
}

/*static int tps65132_write_bytes(unsigned char addr, unsigned char value)*/
int icn6211_write_bytes(unsigned char addr, unsigned char value)
{
	int ret = 0;
	struct i2c_client *client = icn6211_i2c_client;
	char write_data[2] = { 0 };

	printk("\n\n  +++++++++++++++++++++++++   entry %s    +++++++++++++++++++++++\n\n", __func__);

	write_data[0] = addr;
	write_data[1] = value;
	ret = i2c_master_send(client, write_data, 2);
	if (ret < 0)
		LCM_LOGI("icn6211 write data fail !!\n");
	printk("---------- the number of bytes written --success  i2c_master_send ret = %d	-----------  \n", ret);	
	return ret;
}

int icn6211_read_bytes(unsigned char addr, unsigned char *returnData)
{
	int ret = 0;
	struct i2c_client *client = icn6211_i2c_client;
	char data_buffer[2] = { 0 };

	data_buffer[0] = addr;
	
	ret = i2c_master_send(client, &data_buffer[0], 1);
	ret = i2c_master_recv(client, &data_buffer[1], 1);
	
	if (ret < 0)
		LCM_LOGI("icn6211 write data fail !!\n");

	*returnData = data_buffer[1];
	return ret;
}

int icn6211_i2c_read(unsigned char addr, u32 *returnData)
{
	int ret = 0;
	char data;
	ret = icn6211_read_bytes(addr, &data);
	*returnData = (u32)data;
	return ret;
}

static int __init icn6211_iic_init(void)
{
	LCM_LOGI("icn6211_iic_init\n");
#if defined(CONFIG_MTK_LEGACY)
	i2c_register_board_info(ICN6211_I2C_BUSNUM, &icn6211_board_info, 1);
#endif
	LCM_LOGI("icn6211_iic_init2\n");
	i2c_add_driver(&icn6211_iic_driver);
	LCM_LOGI("icn6211_iic_init success\n");
	return 0;
}

static void __exit icn6211_iic_exit(void)
{
	LCM_LOGI("icn6211_iic_exit\n");
	i2c_del_driver(&icn6211_iic_driver);
}

module_init(icn6211_iic_init);
module_exit(icn6211_iic_exit);

MODULE_AUTHOR("Xiaokuan Shi");
MODULE_DESCRIPTION("MTK icn6211 I2C Driver");
MODULE_LICENSE("GPL");
#endif
#endif

// ---------------------------------------------------------------------------
//  Local Constants
// ---------------------------------------------------------------------------
#define FRAME_WIDTH  (800)	//(1920)
#define FRAME_HEIGHT (480)	//(1080)

// ---------------------------------------------------------------------------
//  Local Variables
// ---------------------------------------------------------------------------
static LCM_UTIL_FUNCS lcm_util = {0};
#define SET_RESET_PIN(v)    								(lcm_util.set_reset_pin((v)))
#define UDELAY(n) 											(lcm_util.udelay(n))
#define MDELAY(n) 											(lcm_util.mdelay(n))

// ---------------------------------------------------------------------------
//  LCM Driver Implementations
// ---------------------------------------------------------------------------
static void lcm_set_util_funcs(const LCM_UTIL_FUNCS *util)
{
    memcpy(&lcm_util, util, sizeof(LCM_UTIL_FUNCS));
}

static void lcm_get_params(LCM_PARAMS *params)
{
	memset(params, 0, sizeof(LCM_PARAMS));

	params->type   = LCM_TYPE_DSI;
	params->width  = FRAME_WIDTH;
	params->height = FRAME_HEIGHT;

	params->dsi.mode   = SYNC_EVENT_VDO_MODE;
	// DSI
	/* Command mode setting */		
	params->dsi.LANE_NUM				= LCM_FOUR_LANE;	//4 lane
	//The following defined the fomat for data coming from LCD engine.
	params->dsi.data_format.color_order = LCM_COLOR_ORDER_RGB;	//LCM_COLOR_ORDER_BGR;
	params->dsi.data_format.trans_seq   = LCM_DSI_TRANS_SEQ_MSB_FIRST;
	params->dsi.data_format.padding     = LCM_DSI_PADDING_ON_LSB;
	params->dsi.data_format.format      = LCM_DSI_FORMAT_RGB888; 

	// Highly depends on LCD driver capability.
	// Not support in MT6573
	params->dsi.packet_size=256;

	// Video mode setting		
	//params->dsi.intermediat_buffer_num 	= 2;//2  //because DSI/DPI HW design change, this parameters should be 0 when video mode in MT658X; or memory leakage

	params->dsi.PS=LCM_PACKED_PS_24BIT_RGB888; 
	params->dsi.vertical_sync_active 	= 8;//20; //2;
	params->dsi.vertical_backporch   	= 20;//10; //2;
	params->dsi.vertical_frontporch  	= 20;//18; //4;
	params->dsi.vertical_active_line	= FRAME_HEIGHT; 

	params->dsi.horizontal_sync_active   = 30; //10;
	params->dsi.horizontal_backporch     = 30; //20;
	params->dsi.horizontal_frontporch    = 20; //10;
	params->dsi.horizontal_active_pixel	 = FRAME_WIDTH;
	
	params->dsi.PLL_CLOCK = 167;	
}

#ifdef BUILD_LK
#define EYEPIECE_I2C_ADDR 	0x48		
#define I2C_BUS_NUMBER		3
#define I2C_RETRY_TIMES 	5
static struct mt_i2c_t panel_eyepiece_i2c;
#define H294

u32 panel_eyepiece_i2c_write(kal_uint8 addr, kal_uint8 value)
{
	kal_uint32 ret_code = I2C_OK;
	kal_uint8  write_data[2];
	kal_uint16 len;

	write_data[0] = addr;
	write_data[1] = value;
	
	panel_eyepiece_i2c.id = I2C_BUS_NUMBER;	/* I2C3; */
	/* Since i2c will left shift 1 bit, we need to set FAN5405 I2C address to >>1 */
	panel_eyepiece_i2c.addr = EYEPIECE_I2C_ADDR;
	panel_eyepiece_i2c.mode = ST_MODE;
	panel_eyepiece_i2c.speed = 100;
	len = 2;
	ret_code = i2c_write(&panel_eyepiece_i2c, write_data, len);
	printf("[LK/LCM]%s: panel_eyepiece_i2c_write : ret_code: %d (I2C_OK = 0)\n", __func__, ret_code); 
	if(ret_code == 0)
		printf("[LK/LCM]ret_code = %d , write ok !!!\n", ret_code);
	
	return ret_code;
}

static void panel_eyepiece_init(void)
{
	panel_eyepiece_i2c_write(0x00, 0x01);
						   
	panel_eyepiece_i2c_write(0x02, 0x04);	
	panel_eyepiece_i2c_write(0x03, 0x01);
	panel_eyepiece_i2c_write(0x04, 0x08);

	panel_eyepiece_i2c_write(0x07, 0xFA); //Suggest setting Control ring voltage 
	panel_eyepiece_i2c_write(0x08, 0x05); //Suggest setting Control ring voltage 
	
	panel_eyepiece_i2c_write(0x09, 0xFF); //RGMAP1 Setting	RP
	panel_eyepiece_i2c_write(0x0A, 0xA4); //RGMAP2 Setting
	panel_eyepiece_i2c_write(0x0B, 0x6C); //RGMAP3 Setting
	panel_eyepiece_i2c_write(0x0C, 0x3F); //RGMAP4 Setting
	panel_eyepiece_i2c_write(0x0D, 0x0C); //RGMAP5 Setting
	panel_eyepiece_i2c_write(0x0E, 0x08); //RGMAP6 Setting

	panel_eyepiece_i2c_write(0x0F, 0x00); //RGMAN1 Setting	RN
	panel_eyepiece_i2c_write(0x10, 0x5B); //RGMAN2 Setting
	panel_eyepiece_i2c_write(0x11, 0x93); //RGMAN3 Setting
	panel_eyepiece_i2c_write(0x12, 0XC0); //RGMAN4 Setting
	panel_eyepiece_i2c_write(0x13, 0XF3); //RGMAN5 Setting
	panel_eyepiece_i2c_write(0x14, 0xFF); //RGMAN6 Setting

	panel_eyepiece_i2c_write(0x15, 0xFF); //GGMAP1 Setting	GP
	panel_eyepiece_i2c_write(0x16, 0xA4); //GGMAP2 Setting
	panel_eyepiece_i2c_write(0x17, 0x6C); //GGMAP3 Setting
	panel_eyepiece_i2c_write(0x18, 0x3F); //GGMAP4 Setting
	panel_eyepiece_i2c_write(0x19, 0x0C); //GGMAP5 Setting
	panel_eyepiece_i2c_write(0x1A, 0x08); //GGMAP6 Setting

	panel_eyepiece_i2c_write(0x1B, 0x00); //GGMAN1 Setting	GN
	panel_eyepiece_i2c_write(0x1C, 0x5B); //GGMAN2 Setting
	panel_eyepiece_i2c_write(0x1D, 0x93); //GGMAN3 Setting
	panel_eyepiece_i2c_write(0x1E, 0xC0); //GGMAN4 Setting
	panel_eyepiece_i2c_write(0x1F, 0XF3); //GGMAN5 Setting
	panel_eyepiece_i2c_write(0x20, 0xFF); //GGMAN6 Setting

	panel_eyepiece_i2c_write(0x21, 0xFF); //BGMAP1 Setting	BP
	panel_eyepiece_i2c_write(0x22, 0xA4); //BGMAP2 Setting
	panel_eyepiece_i2c_write(0x23, 0x6C); //BGMAP3 Setting
	panel_eyepiece_i2c_write(0x24, 0x3F); //BGMAP4 Setting
	panel_eyepiece_i2c_write(0x25, 0x0C); //BGMAP5 Setting
	panel_eyepiece_i2c_write(0x26, 0x08); //BGMAP6 Setting

	panel_eyepiece_i2c_write(0x27, 0x00); //BGMAN1 Setting	BN
	panel_eyepiece_i2c_write(0x28, 0x5B); //BGMAN2 Setting
	panel_eyepiece_i2c_write(0x29, 0x93); //BGMAN3 Setting
	panel_eyepiece_i2c_write(0x2A, 0xC0); //BGMAN4 Setting
	panel_eyepiece_i2c_write(0x2B, 0XF3); //BGMAN5 Setting
	panel_eyepiece_i2c_write(0x2C, 0xFF); //BGMAN6 Setting
}

#if 0
static void panel_eyepiece_deinit(void)
{
	struct i2c_adapter *adapter;
	u8 write_data[2] = {0x02, 0x00};
	int ret;
	int retry_times = I2C_RETRY_TIMES;

	adapter = i2c_get_adapter(I2C_BUS_NUMBER);
	if (!adapter) {
		printk("panel-eyepiece: Failed to get I2C adapter, bus %d\n", I2C_BUS_NUMBER);
		return;
	}

deinit_retry:
	ret = panel_eyepiece_i2c_write(adapter,write_data, 2);
	if(ret && retry_times > 0){
		retry_times --;
		msleep(100);
		goto deinit_retry;
	}

	if(!ret)
		printk("panel-eyepiece: panel deinit done\n");
}
#endif
#endif

#ifdef BUILD_LK
#define I2C_CH                I2C3	//channel 3
#define ICN6211_I2C_ADDR      0x2c	//0x58
static struct mt_i2c_t icn6211_i2c;

u32 _icn6211_i2c_read(kal_uint8 addr, kal_uint8 *dataBuffer)
{
	kal_uint32 ret_code = I2C_OK;
	kal_uint16 len;
	*dataBuffer = addr;

	icn6211_i2c.id = I2C_CH;	/* I2C2; */
	/* Since i2c will left shift 1 bit, we need to set FAN5405 I2C address to >>1 */
	icn6211_i2c.addr = (ICN6211_I2C_ADDR);	// >> 1);	//ICN6211_I2C_ADDR;	//
	icn6211_i2c.mode = ST_MODE;
	icn6211_i2c.speed = 100;
	len = 1;

	ret_code = i2c_write_read(&icn6211_i2c, dataBuffer, len, len);

	if (I2C_OK != ret_code)
		dprintf(INFO, "%s: i2c_read: ret_code: %d\n", __func__, ret_code);

	return ret_code;
}

u32 _icn6211_i2c_write(kal_uint8 addr, kal_uint8 value)
{
	kal_uint32 ret_code = I2C_OK;
	kal_uint8  write_data[2];
	kal_uint16 len;

	write_data[0] = addr;
	write_data[1] = value;
	
	icn6211_i2c.id = I2C_CH;	/* I2C2; */
	/* Since i2c will left shift 1 bit, we need to set FAN5405 I2C address to >>1 */
	icn6211_i2c.addr = ICN6211_I2C_ADDR;	//(ICN6211_I2C_ADDR >> 1);
	icn6211_i2c.mode = ST_MODE;
	icn6211_i2c.speed = 100;
	len = 2;
	ret_code = i2c_write(&icn6211_i2c, write_data, len);
	//printf("[LK/LCM]%s: i2c_write: ret_code: %d (I2C_OK = 0)\n", __func__, ret_code); 
	if(ret_code == 0)
		//printf("[LK/LCM]ret_code = %d , write ok !!!\n", ret_code);
	return ret_code;
}

u32 icn6211_reg_i2c_read(u8 addr)
{
    u8 data = 0xFF;
	u32 result_tmp;		
    result_tmp = _icn6211_i2c_read(addr, &data);	
    return data;
}

int icn6211_reg_i2c_write(u8 addr, u8 value)
{
    u32 result_tmp;
    result_tmp = _icn6211_i2c_write(addr, value);
	return result_tmp;
}

//#else
//kernel build
u32 lcm_icn6211_i2c_read(u16 addr)
{
#if 0   
	u32 u4Reg = 0;
    u32 ret_code = 0;

    ret_code = icn6211_i2c_read(addr, &u4Reg);
    if (ret_code != 0)
    {
        return ret_code;
    }

    return u4Reg;
#endif 
	return 0;
}

static void init_icn6211_registers(void)
{
#ifdef BUILD_LK
	//vsync，clk_out
	ICN6211_REG_WRITE(0x20, 0x20);
	ICN6211_REG_WRITE(0x21, 0xE0);
	ICN6211_REG_WRITE(0x22, 0x13);
	ICN6211_REG_WRITE(0x23, 0x14);
	ICN6211_REG_WRITE(0x24, 0x1E);
	ICN6211_REG_WRITE(0x25, 0x1E);
	ICN6211_REG_WRITE(0x26, 0x00);
	ICN6211_REG_WRITE(0x27, 0x14);
	ICN6211_REG_WRITE(0x28, 0x08);
	ICN6211_REG_WRITE(0x29, 0x14);
	ICN6211_REG_WRITE(0x34, 0x80);
	ICN6211_REG_WRITE(0x36, 0x14);
	ICN6211_REG_WRITE(0xB5, 0xA0);
	ICN6211_REG_WRITE(0x5C, 0xFF);
	ICN6211_REG_WRITE(0x2A, 0x01);
	ICN6211_REG_WRITE(0x56, 0x90);	
	ICN6211_REG_WRITE(0x6B, 0x51);	
	ICN6211_REG_WRITE(0x69, 0x23);	
	ICN6211_REG_WRITE(0x10, 0x40);	
	ICN6211_REG_WRITE(0x11, 0x88);	
	ICN6211_REG_WRITE(0xB6, 0x20);
	ICN6211_REG_WRITE(0x51, 0x20);

	//ICN6211_REG_WRITE(0x14, 0x43);	//color test
	//ICN6211_REG_WRITE(0x2A, 0x49);
	
	ICN6211_REG_WRITE(0x09, 0x10);
#endif	  
}
#endif


//#define ON
#ifdef ON
//#ifdef BUILD_LK
//#define CAM_XSHUTDOWN	GPIO236		//SPI1_MO
#define CAM_ID			GPIO237		//SPI1_CS
//#define CAM_AF_EN_N		GPIO234		//SPI1_CK
//#define DVDD			GPIO180		//GPIO_LCM_RST

#define	DISP_OE_RSTB 	GPIO235		//SPI1_MI
#define VCC10_EN		GPIO30		//CAM_CLK0
#define LED_EN			GPIO31		//CAM_CLK1

static void gpio_set(unsigned long pin,unsigned long mode,unsigned long dir)
{
	mt_set_gpio_mode(pin,mode);
    mt_set_gpio_dir(pin,dir);
}

static void init_gpios(void)
{
	gpio_set(DISP_OE_RSTB,  GPIO_MODE_00, GPIO_DIR_OUT);
	gpio_set(VCC10_EN,      GPIO_MODE_00, GPIO_DIR_OUT);
}

static void disp_oe_reset(void)
{
	mt_set_gpio_out(DISP_OE_RSTB,GPIO_OUT_ONE); 
	MDELAY(500);
	mt_set_gpio_out(DISP_OE_RSTB,GPIO_OUT_ZERO); 
	MDELAY(500);
	mt_set_gpio_out(DISP_OE_RSTB,GPIO_OUT_ONE); 
	MDELAY(500);
}

static void lcm_power(bool on)//10V
{	
	mt_set_gpio_out(VCC10_EN,		on?GPIO_OUT_ONE:GPIO_OUT_ZERO); 
	//mt_set_gpio_out(CAM_XSHUTDOWN,	on?GPIO_OUT_ONE:GPIO_OUT_ZERO);
}

static void cam_pw_init(void)
{
	//gpio_set(CAM_XSHUTDOWN, GPIO_MODE_00, GPIO_DIR_OUT);	
	gpio_set(CAM_ID, 		GPIO_MODE_00, GPIO_DIR_OUT);	
	//gpio_set(CAM_AF_EN_N, 	GPIO_MODE_00, GPIO_DIR_OUT);
	//gpio_set(DVDD,			GPIO_MODE_00, GPIO_DIR_OUT);

	//mt_set_gpio_out(CAM_XSHUTDOWN,  GPIO_OUT_ZERO);
	mt_set_gpio_out(CAM_ID, 		GPIO_OUT_ZERO);
	//mt_set_gpio_out(CAM_AF_EN_N, 	GPIO_OUT_ZERO);
	//mt_set_gpio_out(DVDD,		 	GPIO_OUT_ZERO);
}
#endif

static void lcm_init(void)
{
#ifdef BUILD_LK
	//printf("entry %s !!!!!!!!!!!!!!!!!!!!!!!\n", __func__);
#else
	//printk("entry %s !!!!!!!!!!!!!!!!!!!!!!!\n", __func__);
#endif

#ifdef ON
	init_gpios();
	lcm_power(true);
	MDELAY(2);	
	disp_oe_reset();
	cam_pw_init();
#endif

//	SET_RESET_PIN(1);
//	MDELAY(20);
//	SET_RESET_PIN(0);
//	MDELAY(50);
//	SET_RESET_PIN(1);
//	MDELAY(100);
	
#ifdef H294
#ifdef BUILD_LK
	panel_eyepiece_init();		//lcd_init
	MDELAY(100);
	init_icn6211_registers();	//inc6211_init
	MDELAY(100);
#endif
#endif
}

static void lcm_suspend(void)
{	
//	SET_RESET_PIN(1);
//	MDELAY(20);
//	SET_RESET_PIN(0);
//	MDELAY(50);
//	SET_RESET_PIN(1);
//	MDELAY(100);
#ifdef BUILD_LK
#ifdef ON
	lcm_power(false);
	mt_set_gpio_out(VCC10_EN,GPIO_OUT_ZERO); 
	//mt_set_gpio_out(CAM_XSHUTDOWN,GPIO_OUT_ZERO);
	MDELAY(30);
#endif
#endif
}

static void lcm_resume(void)
{
	lcm_init();	
}

LCM_DRIVER bx5b2a_lcm_drv = {
	.name			= "bx5b2a_mipi2rgb_2lane",
	.set_util_funcs = lcm_set_util_funcs,
	.get_params     = lcm_get_params,
	.init           = lcm_init,
	.suspend        = lcm_suspend,
	.resume         = lcm_resume,
	//.compare_id    = lcm_compare_id,
};
